Escape a large maze

Time: O(N^2); Space: O(N); hard

In a 1 million by 1 million grid, the coordinates of each grid square are (x, y) with 0 <= x, y < 10^6.

We start at the source square and want to reach the target square. Each move, we can walk to a 4-directionally adjacent square in the grid that isn’t in the given list of blocked squares.

Return true if and only if it is possible to reach the target square through a sequence of moves.

Example 1:

Input: blocked = [[0,1],[1,0]], source = [0,0], target = [0,2]

Output: False

Explanation:

  • The target square is inaccessible starting from the source square, because we can’t walk outside the grid.

Example 2:

Input: blocked = [], source = [0,0], target = [999999,999999]

Output: True

Explanation:

  • Because there are no blocked cells, it’s possible to reach the target square.

Note:

  • 0 <= len(blocked) <= 200

  • len(blocked[i]) == 2

  • 0 <= blocked[i][j] < 10^6

  • len(source) == len(target) == 2

  • 0 <= source[i][j], target[i][j] < 10^6

  • source != target

[1]:
import collections

class Solution1(object):
    """
    Time:  O(n^2), n is the number of blocked.
    Space: O(n).
    """
    def isEscapePossible(self, blocked, source, target):
        """
        :type blocked: List[List[int]]
        :type source: List[int]
        :type target: List[int]
        :rtype: bool
        """
        R, C = 10**6, 10**6
        directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]

        def bfs(blocks, source, target):
            max_area_surrounded_by_blocks = len(blocks)*(len(blocks)-1)//2
            lookup = set([source])
            if len(lookup) > max_area_surrounded_by_blocks:
                return True

            q = collections.deque([source])
            while q:
                source = q.popleft()
                if source == target:
                    return True

                for direction in directions:
                    nr, nc = source[0] + direction[0], source[1] + direction[1]
                    if not ((0 <= nr < R) and
                            (0 <= nc < C) and
                            (nr, nc) not in lookup and
                            (nr, nc) not in blocks):
                        continue
                    lookup.add((nr, nc))
                    if len(lookup) > max_area_surrounded_by_blocks:
                        return True
                    q.append((nr, nc))
            return False

        return bfs(set(map(tuple, blocked)), tuple(source), tuple(target)) and \
               bfs(set(map(tuple, blocked)), tuple(target), tuple(source))
[2]:
s = Solution1()
blocked = [[0,1],[1,0]]
source = [0,0]
target = [0,2]
assert s.isEscapePossible(blocked, source, target) == False

blocked = []
source = [0,0]
target = [999999,999999]
assert s.isEscapePossible(blocked, source, target) == True